/*******************************************************************************
  Copyright(c) 2009 Geoffrey Hausheer. All rights reserved.

  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the Free
  Software Foundation; either version 2 of the License, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  more details.

  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc., 59
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.

  The full GNU General Public License is included in this distribution in the
  file called LICENSE.

  Contact Information: dso-tonight-at-phracturedblue-dot-com
*******************************************************************************/

package dsotonight;

import java.util.Calendar;

/**
 *
 * @author ghaushe
 */
public class JulianDate {
 /**
  * Returns the Julian day number that begins at noon of
  * this day, Positive year signifies A.D., negative year B.C.
  * Remember that the year after 1 B.C. was 1 A.D.
  *
  * ref :
  *  Numerical Recipes in C, 2nd ed., Cambridge University Press 1992
  */
  // Gregorian Calendar adopted Oct. 15, 1582 (2299161)
  public static int JGREG= 15 + 31*(10+12*1582);
  public static double HALFSECOND = 0.5;

  public static double toJulian(int[] ymd) {
   int year=ymd[0];
   int month=ymd[1]; // jan=1, feb=2,...
   int day=ymd[2];
   int julianYear = year;
   if (year < 0) julianYear++;
   int julianMonth = month;
   if (month > 2) {
     julianMonth++;
   }
   else {
     julianYear--;
     julianMonth += 13;
   }

   double julian = (java.lang.Math.floor(365.25 * julianYear)
        + java.lang.Math.floor(30.6001*julianMonth) + day + 1720995.0);
   if (day + 31 * (month + 12 * year) >= JGREG) {
     // change over to Gregorian calendar
     int ja = (int)(0.01 * julianYear);
     julian += 2 - ja + (0.25 * ja);
   }
   return java.lang.Math.floor(julian);
 }
 public static double toJulian(Calendar cal) {
     return toJulian(new int[] {cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DATE)});
 }

 /**
 * Converts a Julian day to a calendar date
 * ref :
 * Numerical Recipes in C, 2nd ed., Cambridge University Press 1992
 */
 public static int[] fromJulian(double injulian) {
   int jalpha,ja,jb,jc,jd,je,year,month,day;
   double julian = injulian + HALFSECOND / 86400.0;
   ja = (int) injulian;
   if (ja>= JGREG) {
     jalpha = (int) (((ja - 1867216) - 0.25) / 36524.25);
     ja = ja + 1 + jalpha - jalpha / 4;
   }

   jb = ja + 1524;
   jc = (int) (6680.0 + ((jb - 2439870) - 122.1) / 365.25);
   jd = 365 * jc + jc / 4;
   je = (int) ((jb - jd) / 30.6001);
   day = jb - jd - (int) (30.6001 * je);
   month = je - 1;
   if (month > 12) month = month - 12;
   year = jc - 4715;
   if (month > 2) year--;
   if (year <= 0) year--;

   return new int[] {year, month, day};
  }
  public static double[] sunRiseSet(double jDate, double lonW, double latN) {
      double approxJsolar;
      double meanSolarAnom;
      double C;
      double eclipticSolarLon;
      double solarDecl;
      double hourAngle;
      double jSolarTransit;
      double julianSunset;
      double julianSunrise;
 
      int n = (int) ((jDate - 2451545 - 0.0009) - (lonW/360.0) + 0.5);
      approxJsolar = 2451545 + 0.0009 + (lonW/360.0) + n;
      meanSolarAnom = (357.5291 + 0.98560028 * (approxJsolar - 2451545)) % 360;
      C = (1.9148 * Math.sin(Math.toRadians(meanSolarAnom)))
              + (0.0200 * Math.sin(Math.toRadians(2 * meanSolarAnom)))
              + (0.0003 * Math.sin(Math.toRadians(3 * meanSolarAnom)));
      eclipticSolarLon = (meanSolarAnom + 102.9372 + C + 180) % 360;
      solarDecl = Math.toDegrees(Math.asin( Math.sin(Math.toRadians(eclipticSolarLon)) * Math.sin(Math.toRadians(23.45))));
      hourAngle = Math.toDegrees(Math.acos( (Math.sin(Math.toRadians(-0.83))
              - Math.sin(Math.toRadians(latN)) * Math.sin(Math.toRadians(solarDecl)))
              / (Math.cos(Math.toRadians(latN)) * Math.cos(Math.toRadians(solarDecl)))));
      jSolarTransit = approxJsolar + (0.0053 * Math.sin(Math.toRadians(meanSolarAnom)))
              - (0.0069 * Math.sin(Math.toRadians(2 * eclipticSolarLon)));
      approxJsolar = 2451545 + 0.0009 + ((hourAngle + lonW)/360.0) + n;
      julianSunset = approxJsolar
              + (0.0053 * Math.sin(Math.toRadians(meanSolarAnom)))
              - (0.0069 * Math.sin(Math.toRadians(2 * eclipticSolarLon)));
      julianSunrise = jSolarTransit - (julianSunset - jSolarTransit);
      return new double []{julianSunrise, julianSunset};
  }
  public static double localToUTC(double jDate) {
      Calendar cal = Calendar.getInstance();
      int tzOffset = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET);
      return jDate - tzOffset / 1000 / (24 * 3600.0);
  }
  
  public static double UTCtoLocal(double jDate) {
      Calendar cal = Calendar.getInstance();
      int tzOffset = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET);
      return jDate + tzOffset / 1000 / (24 * 3600.0);      
  }

}